
/*
 *  Copyright t lefering, David Given
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  These are the four essential freedoms with GNU GPL software:
 *  1: freedom to run the program, for any purpose
 *  2: freedom to study how the program works, and change it to make it do what you wish
 *  3: freedom to redistribute copies to help your Free Software friends
 *  4: freedom to distribute copies of your modified versions to your Free Software friends
 *   ,           ,
 *  /             \
 * ((__-^^-,-^^-__))
 * `-_---'  `---_-'
 *  `--|o`   'o|--'
 *      \  `  /
 *       ): :(
 *       :o_o:
 *        "-"
 */

#include "globals.h"

struct zprintnode {
	struct zprintnode *next;
	char *value;
};

struct zbuffer {
	struct zprintnode *zfirst;
	struct zprintnode *zlast;
};

static struct zbuffer zbuffers[ZBUFFER__MAX];
static struct zbuffer *currentbuffer = &zbuffers[ZBUFFER_CODE];

char *aprintf(const char *fmt, ...)
{
	char *p = NULL;

	va_list ap;
	va_start(ap, fmt);
	vasprintf(&p, fmt, ap);
	va_end(ap);

	return p;
}

void zprintf(const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	zvprintf(format, ap);
	va_end(ap);
}

void zvprintf(const char *format, va_list ap)
{
	if (!currentbuffer) {
		if (0) {
			vprintf(format, ap);
		} else {
			vfprintf(outputstream, format, ap);
		}
	} else {
		struct zprintnode *node = calloc(1, sizeof(struct zprintnode));
		node->next = NULL;
		vasprintf(&node->value, format, ap);

		if (currentbuffer->zlast)
			currentbuffer->zlast->next = node;
		currentbuffer->zlast = node;

		if (!currentbuffer->zfirst)
			currentbuffer->zfirst = node;
	}
}

void zflush(int buffer)
{
	struct zbuffer *buf = currentbuffer;
	zsetbuffer(buffer);

	while (buf->zfirst) {
		struct zprintnode *node = buf->zfirst;
		buf->zfirst = node->next;

		zprintf("%s", node->value);

		free(node->value);
		free(node);
	}

	buf->zfirst = buf->zlast = NULL;
}

void zsetbuffer(int buffer)
{
	if (buffer == ZBUFFER_STDOUT)
		currentbuffer = NULL;
	else {
		assert((buffer >= 0));
		assert((buffer < ZBUFFER__MAX));

		currentbuffer = &zbuffers[buffer];
	}
}

void dump_bb(struct basic_block *bb)
{
	cg->comment(".L%p // basic block number %d \n", bb, bb->nr);

	struct instruction *insn;
	FOR_EACH_PTR(bb->insns, insn) {
		if (!insn->bb)
			continue;
		cg->comment("  %s\n", show_instruction(insn));
	}
	END_FOR_EACH_PTR(insn);

	cg->comment("\n");
}

/*
 * Symbol type printout. The type system is by far the most
 * complicated part of C - everything else is trivial.
 */
static const char *show_modifiers(unsigned long mod, int term)
{
	static char buffer[100];
	int len = 0;
	int i = 0;
	struct mod_name {
		unsigned long mod;
		const char *name;
	} *m;

	static struct mod_name mod_names[] = {
		{ MOD_AUTO, "auto" },
		{ MOD_EXTERN, "extern" },
		{ MOD_REGISTER, "register" },
		{ MOD_STATIC, "static" },
		{ MOD_INLINE, "inline" },
		{ MOD_CONST, "const" },
		{ MOD_RESTRICT, "restrict" },
		{ MOD_VOLATILE, "volatile" },
		{ MOD_ADDRESSABLE, "[addressable]" },
		{ MOD_ASSIGNED, "[assigned]" },
		{ MOD_ATOMIC, "[atomic]" },
		{ MOD_BITWISE, "[bitwise]" },
		{ MOD_EXPLICITLY_SIGNED, "[explicitly-signed]" },
		{ MOD_GNU_INLINE, "[gnu_inline]" },
		{ MOD_NOCAST, "[nocast]" },
		{ MOD_NODEREF, "[noderef]" },
		{ MOD_NORETURN, "[noreturn]" },
		{ MOD_PURE, "[pure]" },
		{ MOD_SAFE, "[safe]" },
		{ MOD_SIGNED, "[signed]" },
		{ MOD_TLS, "[tls]" },
		{ MOD_TOPLEVEL, "[toplevel]" },
		{ MOD_UNSIGNED, "[unsigned]" },
		{ MOD_UNUSED, "[unused]" },
		{ MOD_USERTYPE, "[usertype]" },
	};

	for (i = 0; i < ARRAY_SIZE(mod_names); i++) {
		m = mod_names + i;
		if (mod & m->mod) {
			char c;
			const char *name = m->name;
			while ((c = *name++) != '\0' && len + 2 < sizeof buffer) {
				buffer[len++] = c;
			}
			buffer[len++] = ' ';
		}
	}
	if (len && !term)	// strip the trailing space
	{
		--len;
	}
	buffer[len] = 0;
	return buffer;
}

void sparse_output_symdata(struct symbol *sym)
{

	cg->comment("symbol = \"%s\"\n", show_ident(sym->ident));
	cg->comment("type   = %d \"%s\"\n", sym->ctype.base_type->type, show_typename(sym));
	cg->comment("modif  = %lx \"%s\"\n", sym->ctype.modifiers, show_modifiers(sym->ctype.modifiers, 0));

	return;
}

/* end */
